﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Text;
using System.Windows.Forms;
using MSNPSharp;
using CGLib;
using CGLib.Enums;

namespace TienLen
{
    public partial class MSNPlayerRoom : UserControl
    {
        #region Fields

        private Contact hostGroup = null;
        private Contact hostContact = null;

        private Label[] labels;
        private PictureBox[] pics;
        private PictureBox[] kickpics;
        private PictureBox[] addpics;

        int countdown = 5;
        Timer timercd = new Timer();

        int gameIndex;

        int aiID = 0;
        int numBots = 0;

        #endregion

        public MSNPlayerRoom(Contact groupHost, Contact host, int gameIndex)
        {
            InitializeComponent();

            #region Set Required Fields

            labels = new Label[4] { p1_name, p2_name, p3_name, p4_name };
            pics = new PictureBox[4] { p1_pic, p2_pic, p3_pic, p4_pic };

            // Since player 1 is the user, we don't need the buttons for that player
            kickpics = new PictureBox[3] { p2_kick, p3_kick, p4_kick };
            addpics = new PictureBox[3] { p2_add, p3_add, p4_add };

            this.hostGroup = groupHost;
            this.hostContact = host;
            this.gameIndex = gameIndex;

            #endregion

            SvrFunc.MSN.MessageManager.TextMessageReceived += new EventHandler<TextMessageArrivedEventArgs>(MessageManager_TextMessageReceived);
            SvrFunc.MSN.Owner.StatusChanged += new EventHandler<StatusChangedEventArgs>(Owner_StatusChanged);

            SvrFunc.MSN.Nameserver.JoinedGroupChat += new EventHandler<GroupChatParticipationEventArgs>(Nameserver_JoinedGroupChat);
            SvrFunc.MSN.Nameserver.LeftGroupChat += new EventHandler<GroupChatParticipationEventArgs>(Nameserver_LeftGroupChat);

            if (host != SvrFunc.MSN.Owner) // Invited user
            {
                ToogleChatEnable(true);

                // Dispose controls that are only for host use
                for (int i = 0; i < kickpics.Length; i++)
                {
                    kickpics[i].Dispose();
                    addpics[i].Dispose();
                }

                contextAdd.Dispose();
                timercd.Dispose();

                if (hostGroup.ClientType == IMAddressInfoType.TemporaryGroup)
                {
                    foreach (KeyValuePair<string, Contact> pair in hostGroup.ContactList[IMAddressInfoType.None])
                        AddPlayer(pair.Value);
                }
                else
                {
                    AddPlayer(hostGroup);
                    AddPlayer(SvrFunc.MSN.Owner);
                }
            }
            else // Host
            {
                AddPlayer(SvrFunc.MSN.Owner);

                btnInvite.Visible = true;
                timercd.Interval = 1000;
                timercd.Tick += new EventHandler(timercd_Tick);

                for (int i = 0; i < SvrFunc.Games[gameIndex].Computers().Length; i++)
                    contextAdd.Items.Add(SvrFunc.Games[gameIndex].Computers()[i].Name());

                #region Add Button Events

                for (int i = 0; i < kickpics.Length; i++)
                {
                    AddButtonEvents(true, i);
                    kickpics[i].MouseClick += new MouseEventHandler(KickButton_MouseClick);
                    kickpics[i].Tag = i + 1;

                    kickpics[i].Image = Image.FromFile(Application.StartupPath + @"\img\other\kickb-normal.png");
                    tpInfo.SetToolTip(kickpics[i], "Kick Player");

                    AddButtonEvents(false, i);
                    addpics[i].MouseClick += new MouseEventHandler(AddButton_MouseClick);
                    addpics[i].Tag = i + 1;

                    addpics[i].Image = Image.FromFile(Application.StartupPath + @"\img\other\addb-normal.png");
                    tpInfo.SetToolTip(addpics[i], "Add AI Player");
                }

                #endregion
            }
        }

        private void AddButtonEvents(bool isKick, int index)
        {
            if (isKick)
            {
                kickpics[index].MouseEnter += new EventHandler(KickButton_MouseEnter);
                kickpics[index].MouseLeave += new EventHandler(KickButton_MouseLeave);
                kickpics[index].MouseDown += new MouseEventHandler(KickButton_MouseDown);
                kickpics[index].MouseUp += new MouseEventHandler(KickButton_MouseUp);
            }
            else
            {
                addpics[index].MouseEnter += new EventHandler(AddButton_MouseLeave);
                addpics[index].MouseLeave += new EventHandler(AddButton_MouseLeave);
                addpics[index].MouseDown += new MouseEventHandler(AddButton_MouseDown);
                addpics[index].MouseUp += new MouseEventHandler(AddButton_MouseUp);
            }
        }

        private void Owner_StatusChanged(object sender, StatusChangedEventArgs e)
        {
            if (InvokeRequired)
            {
                Invoke(new EventHandler<StatusChangedEventArgs>(Owner_StatusChanged), sender, e);
                return;
            }

            // If the user has change status to offline, close the arranged game immediately.
            if (SvrFunc.MSN.Owner.Status == PresenceStatus.Hidden)
                CloseControl();
        }

        private void Nameserver_LeftGroupChat(object sender, GroupChatParticipationEventArgs e)
        {
            if (e.Via != null || e.Via != hostGroup)
                return;

            if (InvokeRequired)
            {
                Invoke(new EventHandler<GroupChatParticipationEventArgs>(Nameserver_LeftGroupChat), sender, e);
                return;
            }

            if (e.Contact == hostGroup)
            {
                new PopUp(AlertType.Notify, e.Contact.Name + " has quit, and you've been taken back to the main menu.").Show();
                CloseControl();

                return;
            }

            RemovePlayer(e.Contact.Account);
        }

        private void Nameserver_JoinedGroupChat(object sender, GroupChatParticipationEventArgs e)
        {
            if (e.Via == null || e.Via != hostGroup)
                return;

            if (InvokeRequired)
            {
                Invoke(new EventHandler<GroupChatParticipationEventArgs>(Nameserver_JoinedGroupChat), sender, e);
                return;
            }

            AppendAnnounceText(e.Contact.Name + " has joined the room");
            AddPlayer(e.Contact);
        }

        private void RemoveButtonEvents(int index)
        {
            kickpics[index].MouseEnter -= KickButton_MouseEnter;
            kickpics[index].MouseLeave -= KickButton_MouseLeave;
            kickpics[index].MouseDown -= KickButton_MouseDown;
            kickpics[index].MouseUp -= KickButton_MouseUp;
        }

        private void timercd_Tick(object sender, EventArgs e)
        {
            if (hostGroup.ContactList == null || countdown == -2)
            {
                timercd.Stop();
                countdown = 5;

                if (hostGroup.ContactList != null) hostGroup.SendMessage(new TextMessage("@TLC MSG B"));
                AppendAnnounceText("Countdown terminated");

                btnStart.Enabled = true;
                btnInvite.Enabled = true;

                if (hostGroup == SvrFunc.MSN.Owner)
                {
                    for (int i = 0; i < addpics.Length; i++)
                        DisableButtons(false, i);
                }
            }
            else
            {
                hostGroup.SendMessage(new TextMessage("@TLC MSG A"));

                if (countdown != 0) AppendAnnounceText("Game starting in " + countdown.ToString());
                countdown--;
            }

            if (countdown == -1)
            {
                timercd.Stop();

                ParentForm.Hide();
                CloseControl();
                new Client((IGame)Activator.CreateInstance(SvrFunc.Games[gameIndex].GetType()), SvrType.MSN, GetPlayers(), hostGroup).Show();

                return;
            }
        }

        private void ToogleChatEnable(bool enabled)
        {
            cbox.Enabled = enabled;
            cwrite.Enabled = enabled;

            cwrite.Text = string.Empty;

            if (hostGroup == SvrFunc.MSN.Owner)
                btnStart.Visible = enabled;
        }

        private void AddPlayer(Contact contact)
        {
            int x = FindEmptySlot();

            pics[x].Image = contact.DisplayImage.Image;
            labels[x].Text = contact.Name;
            labels[x].Tag = contact.Account;

            if (x != 0)
            {
                kickpics[x - 1].Visible = true;
                addpics[x - 1].Visible = false;
                DisableButtons(false, x - 1);
            }

            SetAddSlot();
        }

        /// <summary>
        /// Add an AI player to the game.
        /// </summary>
        private void AddPlayer(string name, int ID)
        {
            int x = FindEmptySlot();

            labels[x].Text = name;
            labels[x].Tag = ID;
            // pics[x].Image = contact.DisplayImage.Image;

            kickpics[x - 1].Visible = true;
            addpics[x - 1].Visible = false;
            DisableButtons(false, x - 1);
            
            numBots++;
            SetAddSlot();
        }

        private void SetAddSlot()
        {
            int x = FindEmptySlot() - 1;

            if (x == -2) return;

            addpics[x].Visible = true;
            kickpics[x].Visible = true;

            DisableButtons(true, x);
        }

        private int FindEmptySlot()
        {
            for (int i = 0; i < 4; i++)
            {
                if (string.IsNullOrEmpty(labels[i].Text))
                    return i;
            }

            return -1;
        }

        /// <summary>
        /// Find the contact with the email given.
        /// </summary>
        /// <returns>The value will return null if the contact doesn't exist in the conversation.</returns>
        private Contact FindContact(string mail)
        {
            // Return the owners contact (since contacts in the conversation don't include owner)
            if (mail == SvrFunc.MSN.Owner.Account)
                return SvrFunc.MSN.Owner;

            return hostGroup.ContactList[IMAddressInfoType.None][mail];
        }

        /// <summary>
        /// The number of players active in the player room.
        /// </summary>
        private int PlayersInGame
        {
            get { return numBots + 1 + (hostGroup.ContactList != null ? 
                hostGroup.ContactList[IMAddressInfoType.None].Count : (hostGroup == SvrFunc.MSN.Owner ? 0 : 1)); }
        }

        private void SortList()
        {
            RESTART:

            for (int i = 0; i < 4; i++)
            {
                if (string.IsNullOrEmpty(labels[i].Text) && labels.Length != 4)
                {
                    pics[i].Image = pics[i + 1].Image;
                    labels[i].Text = labels[i + 1].Text;
                    labels[i].Tag = labels[i + 1].Tag;

                    addpics[i - 1].Visible = false;
                    DisableButtons(false, i - 1);

                    ClearSlot(i + 1);

                    goto RESTART;
                }
            }
        }

        private void ClearSlot(int index)
        {
            pics[index].Image = null;
            labels[index].Text = string.Empty;
            labels[index].Tag = null;

            if (index != 3)
            {
                kickpics[index].Visible = false;
                addpics[index].Visible = false;
            }

            SetAddSlot();
        }

        private void RemovePlayer(int index)
        {
            // If countdown is active, set the message and end the timer tick.
            if (hostGroup == SvrFunc.MSN.Owner && timercd.Enabled)
                countdown = -2;

            int tempRes;
            if (int.TryParse(labels[index].Tag.ToString(), out tempRes))
                numBots--;

            ClearSlot(index);

            if (hostGroup.ContactList[IMAddressInfoType.None] == null)
                ToogleChatEnable(false);

            else
                SortList();
        }

        /// <summary>
        /// Remove the player through searching each labels ID.
        /// </summary>
        private void RemovePlayer(string mailID)
        {
            for (int i = 0; i < 4; i++)
            {
                if (mailID == labels[i].Tag.ToString())
                {
                    RemovePlayer(i);
                    return;
                }
            }
        }

        private void MessageManager_TextMessageReceived(object sender, TextMessageArrivedEventArgs e)
        {
            if (InvokeRequired)
            {
                Invoke(new EventHandler<TextMessageArrivedEventArgs>(MessageManager_TextMessageReceived), sender, e);
                return;
            }

            bool success;
            MSNPacket packet = new MSNPacket((e as TextMessageArrivedEventArgs).TextMessage.Text, out success);

            if (success)
            {
                if (packet.Type == MsgType.Invite)
                {
                    switch (packet.Info[0])
                    {
                        case "B":

                            // Second contact comes into the game
                            if (hostGroup == SvrFunc.MSN.Owner)
                            {
                                hostGroup = e.OriginalSender;
                                hostGroup.SendMessage(new TextMessage("@TLC INV D " + e.Sender.Account + " " + SvrFunc.GameGUID[gameIndex]));

                                AddPlayer(e.OriginalSender);

                                ToogleChatEnable(true);
                            }

                            // Two contacts have joined into the game
                            else if (hostGroup.ClientType != IMAddressInfoType.TemporaryGroup)
                            {
                                List<Contact> tempList = new List<Contact>(new Contact[] { hostGroup, e.Sender });

                                SvrFunc.MSN.Nameserver.CreateMultiparty(tempList,
                                    delegate(object s, MultipartyCreatedEventArgs e2)
                                    {
                                        hostGroup = e2.Group;
                                        e2.Group.SendMessage(new TextMessage("@TLC INV D " + e.Sender.Account + " " + SvrFunc.GameGUID[gameIndex]));
                                    });
                            }

                            // If the player slots aren't full
                            else if (PlayersInGame < 4)
                            {
                                SvrFunc.MSN.Nameserver.InviteContactToMultiparty(e.OriginalSender, hostGroup);
                                e.OriginalSender.SendMessage(new TextMessage("@TLC INV D " + e.Sender.Account + " " + SvrFunc.GameGUID[gameIndex]));
                            }

                            // Send notification to user that the slots are full
                            else
                                e.OriginalSender.SendMessage(new TextMessage("@TLC INV F"));

                            break;

                        case "E":

                            if (packet.Info[1] == SvrFunc.MSN.Owner.Account)
                            {
                                new PopUp(AlertType.Notify, "The owner has kicked you from the game.").Show();
                                CloseControl();

                                return;
                            }

                            break;

                        case "G":

                            if (packet.Info.Length == 4 ||
                                (packet.Info.Length == 5 && packet.Info[4] == SvrFunc.MSN.Owner.Account))
                            {
                                if ((UserType)Convert.ToInt32(packet.Info[2]) == UserType.Player)
                                    AddPlayer(FindContact(packet.Info[3]));
                                else
                                    AddPlayer(packet.Info[2], Convert.ToInt32(packet.Info[3]));
                            }
                            else
                                RemovePlayer(packet.Info[2]);

                            break;

                        case "H":

                            new PopUp(AlertType.Notify, hostGroup.Name + " has quit, and you've been taken back to the main menu.").Show();
                            CloseControl();

                            break;
                    }
                }

                else if (packet.Type == MsgType.Announce)
                {
                    switch (packet.Info[0])
                    {
                        case "A":
                            {
                                if (countdown != 0) AppendAnnounceText("Game starting in " + countdown.ToString());
                                countdown--;

                                if (countdown == -1)
                                {
                                    ParentForm.Hide();
                                    CloseControl();
                                    new Client((IGame)Activator.CreateInstance(SvrFunc.Games[gameIndex].GetType()), SvrType.MSN, GetPlayers(), hostGroup).Show();

                                    return;
                                }

                                break;
                            }
                        case "B":
                            {
                                countdown = 5;
                                AppendAnnounceText("Countdown terminated");
                                break;
                            }
                    }
                }
                else
                {
                    if (hostGroup.ContactList == null ||
                        hostGroup.ContactList != null && e.Sender != hostGroup) return;

                    AppendText(e.Sender.Name, (e as TextMessageArrivedEventArgs).TextMessage.Text);
                }
            }
        }

        private UserType GetUserType(string text)
        {
            int result;

            if (int.TryParse(text, out result))
                return UserType.Computer;
            else if (text == SvrFunc.MSN.Owner.Account)
                return UserType.Host;
            else
                return UserType.Player;
        }

        private int FindIndex(string mailID)
        {
            for (int i = 0; i < PlayersInGame; i++)
            {
                if (labels[i].Tag.ToString() == mailID)
                    return i;
            }

            return -1;
        }

        private Player[] GetPlayers()
        {
            Player[] playuse = new Player[PlayersInGame];

            for (int x = FindIndex(SvrFunc.MSN.Owner.Account), y = 0; y < PlayersInGame; y++)
            {
                int result;
                string mailID = string.Empty;
                UserType userType;

                if (!int.TryParse(labels[x].Tag.ToString(), out result))
                {
                    mailID = FindContact(labels[x].Tag.ToString()).Account;
                    userType = mailID == hostGroup.Account ? UserType.Host : UserType.Player;
                }
                else
                {
                    mailID = result.ToString();
                    userType = UserType.Computer;
                }

                playuse[y] = new Player(labels[x].Text, userType, y + 1, mailID);

                if (x == PlayersInGame - 1)
                {
                    x = 0;
                    continue;
                }
                else x++;
            }

            return playuse;
        }

        private void AppendText(string name, string text)
        {
            if (SvrFunc.MSN.Owner.Name == name)
                cbox.SelectionColor = Color.Red;
            else cbox.SelectionColor = Color.Blue;

            cbox.SelectionFont = new Font(cbox.Font, FontStyle.Bold);
            cbox.AppendText(name + ": ");


            cbox.SelectionColor = Color.Black;
            cbox.SelectionFont = new Font(cbox.Font, FontStyle.Regular);
            cbox.AppendText(text + "\r\n");
        }

        private void AppendAnnounceText(string text)
        {
            cbox.SelectionFont = new Font(cbox.Font, FontStyle.Bold);
            cbox.AppendText(text + "\r\n");
        }

        private void CloseControl()
        {
            SvrFunc.MSN.MessageManager.TextMessageReceived -= MessageManager_TextMessageReceived;
            SvrFunc.MSN.Owner.StatusChanged -= Owner_StatusChanged;

            SvrFunc.MSN.Nameserver.LeftGroupChat -= Nameserver_LeftGroupChat;
            SvrFunc.MSN.Nameserver.JoinedGroupChat -= Nameserver_JoinedGroupChat;

            if (hostGroup.ClientType == IMAddressInfoType.TemporaryGroup)
                SvrFunc.MSN.Nameserver.LeaveMultiparty(hostGroup);
            else
                hostGroup.SendMessage(new TextMessage("@TLC INV H"));

            (ParentForm as MSNClient).playGameButton.Text = "Play Game!";
            timercd.Dispose();
            this.Dispose();
        }

        private void btnClose_Click(object sender, EventArgs e)
        {
            CloseControl();
        }

        private void btnInvite_Click(object sender, EventArgs e)
        {
            if ((ParentForm as MSNClient).OnlineUsers[0].Nodes.Count == 0)
            {
                MessageBox.Show("There are no users online that are running the client.", "Cannot Invite Users", MessageBoxButtons.OK, MessageBoxIcon.Information);
                return;
            }
            else if (PlayersInGame == 4)
                MessageBox.Show("The player room is already full.", "Game Room Full", MessageBoxButtons.OK, MessageBoxIcon.Information);

            else
            {
                InviteUser invitefrm = new InviteUser((ParentForm as MSNClient).OnlineUsers[0].Nodes);

                if (invitefrm.ShowDialog(Parent) == DialogResult.OK)
                {
                    for (int x = 0; x < invitefrm.InvitedContacts.Length; x++)
                    {
                        invitefrm.InvitedContacts[x].SendMessage(new TextMessage("@TLC INV A " + SvrFunc.GameGUID[gameIndex].ToString()));
                    }
                }
            }
        }

        #region Button Events

        private void KickButton_MouseEnter(object sender, EventArgs e)
        {
            (sender as PictureBox).Image = Image.FromFile(Application.StartupPath + @"\img\other\kickb-hover.png");
        }
        private void AddButton_MouseEnter(object sender, EventArgs e)
        {
            (sender as PictureBox).Image = Image.FromFile(Application.StartupPath + @"\img\other\addb-hover.png");
        }

        private void KickButton_MouseLeave(object sender, EventArgs e)
        {
            (sender as PictureBox).Image = Image.FromFile(Application.StartupPath + @"\img\other\kickb-normal.png");
        }

        private void AddButton_MouseLeave(object sender, EventArgs e)
        {
            (sender as PictureBox).Image = Image.FromFile(Application.StartupPath + @"\img\other\addb-normal.png");
        }

        private void KickButton_MouseDown(object sender, MouseEventArgs e)
        {
            (sender as PictureBox).Image = Image.FromFile(Application.StartupPath + @"\img\other\kickb-hold.png");
        }

        private void AddButton_MouseDown(object sender, MouseEventArgs e)
        {
            (sender as PictureBox).Image = Image.FromFile(Application.StartupPath + @"\img\other\addb-hold.png");
        }

        private void KickButton_MouseUp(object sender, MouseEventArgs e)
        {
            (sender as PictureBox).Image = Image.FromFile(Application.StartupPath + @"\img\other\kickb-hover.png");
        }

        private void AddButton_MouseUp(object sender, MouseEventArgs e)
        {
            (sender as PictureBox).Image = Image.FromFile(Application.StartupPath + @"\img\other\addb-hover.png");
        }

        private void KickButton_MouseClick(object sender, MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Left)
            {
                int index = (int)(sender as PictureBox).Tag;
                int result;

                if (!int.TryParse(labels[index].Tag.ToString(), out result))
                {
                    Contact kickCon = FindContact(labels[index].Tag.ToString());
                    if (hostGroup.ContactList != null) hostGroup.SendMessage(new TextMessage("@TLC INV E " + kickCon.Account));
                }
                else
                    if (hostGroup.ContactList != null) hostGroup.SendMessage(new TextMessage("@TLC INV G 1 " + result.ToString()));

                RemovePlayer(index);
            }
        }

        private void AddButton_MouseClick(object sender, MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Left)
            {
                if (!contextAdd.Visible)
                {
                    Point point = (sender as PictureBox).PointToScreen(new Point(0, (sender as PictureBox).Size.Height));

                    contextAdd.Show(point);
                    contextAdd.Tag = (sender as PictureBox).Tag;
                }
                else contextAdd.Hide();
            }
        }

        #endregion

        private void csend_Click(object sender, EventArgs e)
        {
            hostGroup.SendMessage(new TextMessage(cwrite.Text));

            AppendText(SvrFunc.MSN.Owner.Name, cwrite.Text);
            cwrite.Text = string.Empty;
        }

        private void cwrite_KeyPress(object sender, KeyPressEventArgs e)
        {
            if (e.KeyChar == '\r') csend_Click(sender, EventArgs.Empty);
        }

        private void cwrite_TextChanged(object sender, EventArgs e)
        {
            csend.Enabled = !string.IsNullOrEmpty(cwrite.Text);
        }

        private void DisableButtons(bool disable, int index)
        {
            if (kickpics[index].Visible)
            {
                if (disable)
                    RemoveButtonEvents(index);
                else
                    AddButtonEvents(true, index);

                kickpics[index].Cursor = disable ? Cursors.Default : Cursors.Hand;
                kickpics[index].Image = Image.FromFile(Application.StartupPath + @"\img\other\kickb-" +
                    (disable ? "disb" : "normal") + ".png");
                
                kickpics[index].Enabled = !disable;

                if (!timercd.Enabled) return;
            }

            if (addpics[index].Visible)
            {
                addpics[index].Cursor = disable ? Cursors.Default : Cursors.Hand;
                addpics[index].Image = Image.FromFile(Application.StartupPath + @"\img\other\addb-" +
                    (disable ? "disb" : "normal") + ".png");

                addpics[index].Enabled = !disable;
            }
        }

        private void btnStart_Click(object sender, EventArgs e)
        {
            if (hostGroup.ContactList == null)
                MessageBox.Show("You need two or more players to start the game.", "Unable to Start Game", MessageBoxButtons.OK, MessageBoxIcon.Information);
            else
            {
                timercd.Start();
                btnStart.Enabled = false;
                btnInvite.Enabled = false;

                if (hostGroup == SvrFunc.MSN.Owner)
                {
                    for (int i = 0; i < addpics.Length; i++)
                        DisableButtons(true, i);
                }
            }
        }

        private void contextAdd_ItemClicked(object sender, ToolStripItemClickedEventArgs e)
        {
            AddPlayer(e.ClickedItem.Text, aiID);

            if (hostGroup.ContactList != null)
                hostGroup.SendMessage(new TextMessage("@TLC INV G 0 " + e.ClickedItem.Text + " " + aiID.ToString()));

            aiID++;
        }
    }
}
